﻿namespace JoinBox.Forms;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

public enum RunCadVer
{
    Minimum,
    Maximum,
    All,
}

public class AcadProcess
{
    #region 成员
    const string _acad = "acad";
    List<AcadClsIdInfo> _acadClsIdInfos;

    /// <summary>
    /// 获取已经打开的cad程序
    /// </summary>
    public static Process[] ProcessArray { get => Process.GetProcessesByName(_acad); }
    #endregion

    #region 构造
    public AcadProcess()
    {
        _acadClsIdInfos = new();

        /*这里似乎没有用*/
        // string ProgID = "AutoCAD.Application";
        //// 管理员模式必须要完整的版本号
        // if (ProcessHelper.IsAdministrator())
        // {
        //    var apks = AcadClsIdInfo.AcadProductKeys;
        //    if (apks.Count == 0)
        //        return;
        //    ProgID += "." + apks[apks.Count - 1].Version;
        // }
    }
    #endregion

    #region 方法

    /// <summary>
    /// 启动Acad程序,指定版本
    /// </summary>
    /// <param name="runCadVer">启动什么版本</param>
    public void Run(RunCadVer runCadVer = RunCadVer.Minimum)
    {
        const string acadexe = "\\acad.exe";
        string progID = "AutoCAD.Application.";

        // 获取本机cad路径
        var apks = AcadClsIdInfo.AcadProductKeys;
        if (apks is null || apks.Count == 0)
            return;

        string exePath;
        switch (runCadVer)
        {
            case RunCadVer.Minimum:
                {
                    var apk = apks[0];
                    progID += apk.Version;
                    exePath = apk.Location + acadexe;
                    var acadApp = StartApplicat.Run(progID, exePath);
                    _acadClsIdInfos.Add(new AcadClsIdInfo(new Guid(), acadApp, apk));
                }
                break;
            case RunCadVer.Maximum:
                {
                    var apk = apks[apks.Count - 1];
                    progID += apk.Version;
                    exePath = apk.Location + acadexe;
                    var acadApp = StartApplicat.Run(progID, exePath);
                    _acadClsIdInfos.Add(new AcadClsIdInfo(new Guid(), acadApp, apk));
                }
                break;
            case RunCadVer.All:
                {
                    foreach (var apk in apks)
                    {
                        var acadApp = StartApplicat.Run(progID + apk.Version, apk.Location + acadexe);
                        exePath = apk.Location + acadexe;
                        _acadClsIdInfos.Add(new AcadClsIdInfo(new Guid(), acadApp, apk));
                    }
                }
                break;
        }
    }

    /// <summary>
    /// 这里获取所有已经记录GUID的cad,没有记录将会由启动最新的时候加入
    /// </summary>
    void GetClsIdInfos()
    {
        AcadClsIdHelper.GetActiveAcadCom(_acadClsIdInfos);
        if (_acadClsIdInfos.Count > 1)// 启动cad的时候会是1,减少运算
        {
            // 开启了Acad08,那么它是17.1,此时17也会存在,
            // 所以会有两个obj被保存,需要避免发送两次命令到同一个cad内,
            // 因此,此处需要过滤同系列号的,举出最高版本.
            // 排序17.2>17.1>17>16.1>16,将17.2保留,删除17.1>17,保留16.1,删除16
            _acadClsIdInfos = _acadClsIdInfos.OrderByDescending(item => item.VerNumber).ToList();
            for (int i = 0; i < _acadClsIdInfos.Count; i++)
            {
                double iVer = _acadClsIdInfos[i].VerNumber;
                var decimalPart = iVer - (int)iVer;// 小数部分

                for (int j = i + 1; j < _acadClsIdInfos.Count; j++)
                {
                    var vector = iVer - _acadClsIdInfos[j].VerNumber;
                    if (vector == 0)// 开启了同一个版本
                        continue;
                    if (vector <= decimalPart)// 比较小的版本
                    {
                        _acadClsIdInfos.Remove(_acadClsIdInfos[j]);
                        j--;
                    }
                    else
                        break;
                }
            }
        }
    }

    /// <summary>
    /// 通过cad的com进行反射调用VBA函数获取包围盒
    /// </summary>
    public void GetBoundingBox()
    {
        // 使用 _acadComs.Add 将导致重复获取了com和启动时的,所以消重.
        GetClsIdInfos();// 遍历当前
        if (_acadClsIdInfos.Count == 0)
        {
            Run();
            // GetAcadComs();// 可以看到Run加入和ClsId加入的区别,用来测试消重是否正确
        }
        // com服务器不支持多个同版本,那就消重,或许是我不知道com正确操作?如果可以就取消消重.
        _acadClsIdInfos = _acadClsIdInfos.Distinct(new AcadClsIdInfoDistinct()).ToList();

        int testFlag = 0;// 测试标记
        foreach (var acadClsIdInfo in _acadClsIdInfos)
        {
            if (testFlag == 0)
            {
                var acadCom = acadClsIdInfo.Com;
                var acadComType = acadCom.GetType();

                // 参数,需要设置值
                var args = new object[1];
                args[0] = true;
                // 设置属性-可视,显示窗体
                acadComType.InvokeMember("Visible", BindingFlags.SetProperty, null, acadCom, args);

                // 获取属性
                var comAcDocument = acadComType.InvokeMember("ActiveDocument", BindingFlags.GetProperty, null, acadCom, null);
                var comAcMsSpace = acadComType.InvokeMember("ModelSpace", BindingFlags.GetProperty, null, comAcDocument, null);
                // 调用VBA函数也就是com暴露的函数,画在"激活的文档"的"模型空间"然后输入画一条线的坐标数据.
                var lines = new object[] { new double[] { 100, 100, 0 }, new double[] { 300, 300, 0 } };
                var comAcLine = acadComType.InvokeMember("AddLine", BindingFlags.InvokeMethod, null, comAcMsSpace, lines);

                // pts就是包围盒返回的点集
                var pts = new object[2] { null!, null! };

                // 由于需要从参数中返回结果,所以需要设置 ParameterModifier 作用在 InvokeMember 上
                var paramMod = new ParameterModifier(2);
                paramMod[0] = true;// 设置为true才能改写
                paramMod[1] = true;

                // 求得这条线的包围盒,返回给pts.
                acadComType.InvokeMember("GetBoundingBox",
                    BindingFlags.SuppressChangeType | BindingFlags.InvokeMethod,
                    null, comAcLine, pts, new ParameterModifier[] { paramMod }, null, null);

                // 全屏显示
                acadComType.InvokeMember("ZoomAll", BindingFlags.InvokeMethod, null, acadCom, null);

                var a = (double[])pts[0];
                var b = (double[])pts[1];
                Debug.WriteLine($"MinPoint={a[0]},{a[1]},{a[2]}");
                Debug.WriteLine($"MaxPoint={b[0]},{b[1]},{b[2]}");
            }
            else
            {
#if NET50
                // c#4等效代码
                dynamic acadCom = acadClsIdInfo.Com;
                dynamic acadDoc = acadCom.ActiveDocument;
                if (acadDoc is not null)
                {
                    acadDoc.SendCommand("_.Line 100,100 300,300  ");
                    dynamic acMsSpace = acadDoc.ModelSpace;
                    double[] p1 = new double[3] { 100.0, 100.0, 0.0 };
                    double[] p2 = new double[3] { 300.0, 300.0, 0.0 };

                    dynamic acLine = acMsSpace.AddLine(p1, p2);
                    acLine.GetBoundingBox(out object ptMin, out object ptMax);
                    Debug.WriteLine(ptMin.ToString() + "\n" + ptMax.ToString());
                }
                acadCom.ZoomAll();
#endif
            }
        }
    }
    #endregion
}
